package sailhero

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/looplab/fsm"
	"github.com/shopspring/decimal"
	"github.com/wsw188440873/ctrl-cali/hub"
	"github.com/wsw188440873/ctrl-cali/model"
	"github.com/wsw188440873/ctrl-cali/types"
	"iot-base/common/common"
	"iot-base/common/logger"
	"strconv"
	"strings"
	"time"
)

// { 0: '正在执行', 1: '执行成功', 2: '执行失败', 3: '已取消' 4. 睡眠 };
const (
	InitialStatus = iota
	SuccessStatus
	FailureStatus
	CancelStatus
	SleepStatus
)

var StopTaskErr = errors.New("stop task")

// CaliTasks 执行时间.
type CaliTasks struct {
	stationId     string //站点ID
	thingId       string //设备ID
	calibratorId  string //校准仪ID
	modelId       string //物模型ID
	Curr          int    //当前执行的任务
	TotalProgress int    //任务总数
	requestId     string
	finishChan    chan error //完成
	ctx           context.Context
	// 自定义参数, 一次只能执行一个任务，不存在并发写map.  计数
	Params   map[string]interface{}
	TaskFrom string //任务来源
}

func NewCaliTask(stationId, thingId, calibratorId, modelId, requestId string) *CaliTasks {
	return &CaliTasks{
		stationId:    stationId,
		thingId:      thingId,
		calibratorId: calibratorId,
		modelId:      modelId,
		requestId:    requestId,
		Params:       make(map[string]interface{}),
		finishChan:   make(chan error),
	}
}

////SetRunParams 设置运行任务需要参数.
//func (c *CaliTasks) SetRunParams(stationId, thingId, modelId, requestId string) {
//	c.stationId = stationId
//	c.stationId = thingId
//	c.modelId = modelId
//	c.requestId = requestId
//}

func (c *CaliTasks) Run(pid, taskType string, taskName string, painType string, param types.GroupTask) (string, error) {
	// 拿到锁才能执行任务.
	isLock := hub.TaskManager.Lock(taskType + painType)
	if isLock == true {
		// 用来取消操作
		c.ctx = hub.TaskManager.Ctx
		// 添加操作日志.
		reqData := map[string]interface{}{
			"station_id": c.stationId,
			"model_id":   c.modelId,
			"task_type":  taskType,
		}
		// 手动操作的才能 更新 RunParams， RunParams 提供前端查询实时进度

		isSimple := types.IsCali(taskType) //是否是简单任务

		var total int
		if isSimple {
			total = 1
		} else {
			total = types.GetTaskTotalNum(taskType)
		}

		taskId, err := c.addLog(InitialStatus, "", taskName, taskType, painType, "", 1, total, pid)
		if err != nil {
			// 释放锁
			hub.TaskManager.Unlock(taskType + painType)
			logger.SLog.Errorf("[%s]  c.addLog err= %v", c.requestId, err)
			return "", errors.New("添加任务失败")
		}
		// 简单校准.
		if isSimple {
			if param.Subtasks == nil {
				hub.TaskManager.Unlock(taskType + painType)
				c.addLog(FailureStatus, "参数错误", taskName, taskType, painType, "", 1, len(types.CaliMapEvent[taskType]), pid)
				logger.SLog.Errorf("[%s]  c.addLog err= %v", c.requestId, err)
				return "", errors.New("添加任务失败")
			}

			go c.caliTask(taskId, taskType, painType, param.Subtasks[0])
		} else {
			// 复杂任务.
			FSM := fsm.NewFSM(
				"1",
				types.CaliMapEvent[taskType],
				nil,
			)
			go c.complexTask(pid, param.GroupId, taskId, taskType, painType, FSM)
		}

		// 初始化参数.
		c.Curr = 0
		c.TotalProgress = 0
		c.Params = reqData
		c.TaskFrom = painType
		c.SetManualRunParams(taskType)

		return taskId, nil
	} else {
		// 获取锁失败.
		_, err := c.addLog(FailureStatus, "任务冲突，当前有任务正在运行.", taskName, taskType, painType, "", 1, len(types.CaliMapEvent[taskType]), pid)
		if err != nil {
			logger.SLog.Errorf("[%s]  c.addLog err= %v", c.requestId, err)
			return "", errors.New("任务冲突，当前有任务正在运行")
		}
	}
	return "", nil
}

//caliTicker 校准任务.
func (c *CaliTasks) caliTask(taskId string, taskType, painType string, params types.CaliParams) {
	defer hub.TaskManager.Unlock(taskType + painType)
	//c.Params["task_params"] = params
	err := c.runSubTask(taskId, "1", taskType, params, 0, true)
	if errors.Is(err, StopTaskErr) {
		c.endTask(params, taskType)
		c.modifyLog(CancelStatus, "停止任务", taskId, "", taskType, types.End, errors.New("停止任务"))
		return
	}

	if err != nil {
		logger.SLog.Errorf("[%s]  c.runSubTask err= %v, msg=%v", c.requestId, err)
		c.modifyLog(FailureStatus, err.Error(), taskId, "", taskType, types.End, err)
		return
	}

	c.modifyLog(SuccessStatus, "任务执行完成", taskId, "", taskType, types.Confirm, nil)
	return
}

// complexTask 复杂任务
func (c *CaliTasks) complexTask(pid, groupId string, taskId string, taskType, painType string, fsm *fsm.FSM) {
	defer hub.TaskManager.Unlock(taskType + painType)
	c.Curr = 0
	c.TotalProgress = types.GetTaskTotalNum(taskType)

	// parent 进入执行状态
	if pid != "" {
		c.modifyParentLog(InitialStatus, "", pid, nil)
	}
	// 运行任务组
	go c.runGroupTask(taskId, taskType, fsm)
	// 阻塞等待任务完成
	select {
	case err := <-c.finishChan:
		code := SleepStatus
		if groupId == types.ZeroDriftAndSpanDriftHeaven {
			code = SuccessStatus
		}
		if pid != "" {
			c.modifyParentLog(code, "", pid, err)
		}
		return
	case <-c.ctx.Done():
		// todo 业务调整，这里只需要停止当天的任务, 任务进入休眠状态
		code := SleepStatus
		if groupId == types.ZeroDriftAndSpanDriftHeaven {
			code = SuccessStatus
		}
		if pid != "" {
			c.modifyParentLog(code, "", pid, errors.New("取消任务"))
		}
		return
	}
}

func (c *CaliTasks) runGroupTask(taskId string, taskType string, fsm *fsm.FSM) error {
	select {
	case <-c.ctx.Done():
		// 结束任务
		c.modifyLog(CancelStatus, "停止任务", taskId, "", taskType, types.End, errors.New("手动取消任务"))
		return nil
	default:
		// 获取当前任务状态
		groupId := fsm.Current()
		// 获取组任务配置
		params := types.CaliTaskPrams[taskType][groupId]
		//c.Params["task_params"] = params
		// 开始运行子任务
		for index, taskParam := range params.Subtasks {
			c.Curr++
			err := c.runSubTask(taskId, groupId, taskType, taskParam, index, false)
			// 判断任务是否手动停止
			if errors.Is(err, StopTaskErr) {
				return nil
			}
			if err != nil {
				////24小时量程校准不需要直接结束,需要执行7天 (需要执行下一天的任务组)
				//if taskType == types.ZeroDriftAndSpanDrift {
				//	c.modifyLog(FailureStatus, "任务执行失败", taskId, "", taskType, types.End, err)
				//	c.finishChan <- nil
				//	return err
				//}
				c.modifyLog(FailureStatus, "任务执行失败", taskId, "", taskType, types.End, err)
				c.finishChan <- err
				return err
			}
		}
		//状态转换，进入下一个任务
		err := fsm.Event(groupId)
		if err != nil {
			logger.SLog.Errorf("[%s]  fsm.Event msg=%v", c.requestId, err)
			c.modifyLog(FailureStatus, "异常错误,(切换任务失败)", taskId, "", taskType, types.End, err)
			c.endTask(types.CaliParams{}, taskType)
			c.finishChan <- errors.New("异常错误,(切换任务失败)")
			return errors.New("异常错误,(切换任务失败)")
		}

		status := fsm.Current() // 获取当前最新状态. (判断任务是否完成)
		if status == "END" {
			c.finish(types.CaliParams{}, taskType)
			c.modifyLog(SuccessStatus, "成功", taskId, "", taskType, types.End, err)
			c.finishChan <- nil
			return nil
		}
		// 创建一个计时器(组任务之间是否需要等待)
		if params.IntervalTime > 0 && status != "END" {
			fmt.Println("进入下一天任务")
			t := time.NewTimer(time.Second * time.Duration(params.IntervalTime))
			defer t.Stop()
			<-t.C
		}
		// 进入下一组任务
		go c.runGroupTask(taskId, taskType, fsm)
		return nil
	}

}

//是否存在  当前
func isExist(curr int, array []int) bool {
	if len(array) == 0 {
		return false
	}
	for _, v := range array {
		if curr == v {
			return true
		}
	}
	return false
}

func getGasNum(modelId string, params types.CaliParams) int {
	// 通气
	var number int
	if params.Number > 1 {
		// 正常数值
		number = int(params.Number)
	} else {
		// 百分比
		rang := types.Range[modelId]
		number = int(params.Number * rang)
	}
	return number
}

func getCaliFlow(modelId string, params types.CaliParams) int {
	var caliFlow int
	if params.Flow == 0 {
		caliFlow = types.CaliFlowDefault[modelId]
	} else {
		caliFlow = params.Flow
	}
	return caliFlow
}

func (c *CaliTasks) startTask(params types.CaliParams, taskType string, caliFlow int) (int, error) {

	////todo test
	//return types.Success, nil

	var err error
	caliLogic := NewCalibratorLogic(c.stationId, c.thingId, c.calibratorId, c.requestId)
	number := getGasNum(c.modelId, params)
	code := types.Success
	for i := 0; i < 3; i++ {
		code, err = caliLogic.StartCaliTask(params.GasType, c.stationId, c.modelId, number, types.GetUnit(c.modelId), caliFlow)
		if code == types.Success {
			return code, err
		}
		// 休眠 1秒
		time.Sleep(time.Second)
	}
	return code, err
}

func (c *CaliTasks) endTask(params types.CaliParams, taskType string) (int, error) {
	////todo test
	//return types.Success, nil
	var err error
	caliLogic := NewCalibratorLogic(c.stationId, c.thingId, c.calibratorId, c.requestId)

	var number int
	if params.Number > 1 {
		// 正常数值
		number = int(params.Number)
	} else {
		// 百分比
		rang := types.Range[c.modelId]
		number = int(params.Number * rang)
	}
	code := types.Success
	for i := 0; i < 3; i++ {
		code, err = caliLogic.EndCaliTask(params.GasType, c.stationId, c.modelId, number)
		if code == types.Success {
			return code, err
		}
		// 休眠 1秒
		time.Sleep(time.Second)
	}
	logger.SLog.Infof("[%v]  caliLogic.EndCaliTask code = %v", c.requestId, code)
	return code, err
}

//confirmCali 确认校准.
func (c *CaliTasks) confirmCali(params types.CaliParams, taskType string) (int, error) {
	caliLogic := NewCalibratorLogic(c.stationId, c.thingId, c.calibratorId, c.requestId)
	var err error
	var number int
	if params.Number > 1 {
		// 正常数值
		number = int(params.Number)
	} else {
		// 百分比
		rang := types.Range[c.modelId]
		number = int(params.Number * rang)
	}

	code := types.Success
	for i := 0; i < 3; i++ {
		code, err = caliLogic.ConfirmCali(params.GasType, c.stationId, c.modelId, number)
		if code == types.Success {
			return code, err
		}
	}

	logger.SLog.Infof("[%v]  caliLogic.confirmCali code = %v", c.requestId, code)
	return code, err
}

// dataCollection 采集数据.
func (c *CaliTasks) dataCollection(taskType string, taskId string, params types.CaliParams) (float64, error) {
	caliLogic := NewCalibratorLogic(c.stationId, c.thingId, c.calibratorId, c.requestId)
	valueStr, err := caliLogic.GetValue()

	logger.SLog.Infof("[%v]  caliLogic.GetValue(%v, %v) result = %v , err = %v", c.requestId, c.stationId, c.modelId, valueStr, err)
	if valueStr == "" {
		return 0, err
	}
	d, err := decimal.NewFromString(valueStr)
	value, _ := d.Float64()
	c.saveData(value, params) // 保存在内存中，任务结束后直接计算
	return value, nil
}

func (c *CaliTasks) saveData(value float64, params types.CaliParams) bool {
	var key string
	if params.GasType == 0 {
		key = "0"
	} else {
		rang := types.Range[c.modelId]
		if params.Number > 1 {
			key = strconv.Itoa(int(params.Number))
		} else {
			key = strconv.Itoa(int(params.Number * rang))
		}
	}
	if _, ok := c.Params["collect_data"]; !ok {
		temp := make(map[string][]float64)
		temp[key] = make([]float64, 0, 3)
		temp[key] = append(temp[key], value)
		c.Params["collect_data"] = temp
	} else {
		temp := c.Params["collect_data"].(map[string][]float64)
		temp[key] = append(temp[key], value)
		c.Params["collect_data"] = temp
	}
	return true
}

// Finish 完成.
func (c *CaliTasks) finish(params types.CaliParams, taskType string) bool {
	defer func() {
		if err := recover(); err != nil {
			logger.SLog.Infof("[%v]  finish() err = %v", c.requestId, err)
		}
	}()
	// 计算具体的测试值.
	switch taskType {
	case types.IndicationError:
		// IndicationError 示值误差.  // 成功
		value, isSuccess := indicationError(c.modelId, c.Params)
		if isSuccess == true {
			valueStr := decimal.NewFromFloat(value).Round(2).String()
			c.Params["value"] = valueStr + "%"
			c.Params["raw_value"] = value
		}
		return true
	case types.LinearityError:
		// LinearityError 线性误差
		value, isSuccess := linearityError(c.modelId, c.Params)
		if isSuccess == true && len(value) > 0 {
			valueStr := decimal.NewFromFloat(value[0]).Round(2).String()
			c.Params["value"] = valueStr
			c.Params["raw_value"] = value
		}
		return true
	case types.Precision:
		// Precision 精密度.
		value, isSuccess := precision(c.modelId, c.Params, len(params.ValueTime))
		if isSuccess == true {
			c.Params["raw_value"] = value
			valueStr := ""
			for k, v := range value {
				ppbValue, _ := strconv.Atoi(k)
				//valueStr += fmt.Sprintf("P%v:%v", float64(ppbValue)/500*100, fmt.Sprintf("%0.2f", v)) + ","
				valueStr += fmt.Sprintf("P%v:%v", float64(ppbValue)/500*100, decimal.NewFromFloat(v).Round(2).String()) + ","
			}
			c.Params["value"] = strings.Trim(valueStr, ",")
		}
	case types.ZeroPointNoise:
		value, isSuccess := zeroPointNoiseCalculate(c.modelId, c.Params)
		if isSuccess == true {
			valueStr := decimal.NewFromFloat(value).Round(2).String()
			c.Params["value"] = valueStr
			c.Params["raw_value"] = value
		}
	case types.RangeNoise:
		value, isSuccess := rangeNoiseCalculate(c.modelId, c.Params)
		if isSuccess == true {
			valueStr := decimal.NewFromFloat(value).Round(2).String()
			c.Params["value"] = valueStr
			c.Params["raw_value"] = value
		}
	case types.ZeroDriftAndSpanDrift:
		value, isSuccess := ZeroDriftAndSpanDriftSubTask(c.modelId, c.Params)
		if isSuccess == true {
			c.Params["raw_value"] = value
			valueStr := ""
			for k, v := range value {
				valueStr += fmt.Sprintf("%v:%v", k, decimal.NewFromFloat(v).Round(2).String()) + ","
			}
			c.Params["value"] = strings.Trim(valueStr, ",")
		}

	}
	return true
}

//code 0 初始状态, 1 成功, 2 失败
//msg 错误消息
//taskId 任务ID
//taskName 任务昵称
//taskType 任务类型
//event 事件
//currNum 进度
//modifyLog 修改日志.
func (c *CaliTasks) modifyLog(code int, msg string, taskId string, taskName string, taskType string, event string, err error) {
	c.Params["request_id"] = c.requestId
	c.Params["code"] = code
	if err == nil {
		c.Params["msg"] = ""
	} else {
		c.Params["msg"] = err.Error()
	}
	data := model.TaskLog{
		StationId: c.stationId,
		TaskType:  taskType,
		Status:    code,
		CurrNum:   c.Curr,
		Msg:       msg,
		TaskName:  taskName,
	}
	if code == FailureStatus || code == SuccessStatus || code == CancelStatus {
		data.TaskStatus = "done"
	}
	hub.TaskManager.SetTaskCurr(c.Curr) // 设置任务进度
	// 手动操作的才能 更新 RunParams， RunParams 提供前端查询实时进度
	c.SetManualRunParams(taskType)
	b, _ := json.Marshal(c.Params)
	_ = data.Content.Scan(b)
	model.Dao.TTask.Update(taskId, data)
	//同步消息状态
	data.ID = taskId
	sendCloudModifyTaskLog(data)
}

func (c *CaliTasks) modifyParentLog(code int, msg string, taskId string, err error) {
	c.Params["request_id"] = c.requestId
	c.Params["code"] = code
	if err == nil {
		c.Params["msg"] = ""
	} else {
		c.Params["msg"] = err.Error()
	}
	data := model.TaskLog{
		StationId: c.stationId,
		Status:    code,
		Msg:       msg,
	}
	c.Params["value"] = ""
	delete(c.Params, "raw_value")
	//delete(c.Params, "task_params")
	delete(c.Params, "collect_data")
	if code == FailureStatus || code == SuccessStatus {
		data.TaskStatus = "done"
	}
	if code == SleepStatus {
		data.TaskStatus = "sleep"

	}
	// 初始状态才累加
	if code == InitialStatus {
		model.Dao.TTask.AdUpdateCurrNum(taskId)
	}
	if code == SuccessStatus {
		value, _ := ZeroDriftAndSpanDrift(taskId)
		c.Params["value"] = value
	}

	b, _ := json.Marshal(c.Params)
	_ = data.Content.Scan(b)
	model.Dao.TTask.Update(taskId, data)
	//同步消息状态
	data.ID = taskId
	sendCloudModifyTaskLog(data)
}

// SetManualRunParams 设置手动运行的参数
func (c *CaliTasks) SetManualRunParams(taskType string) {
	// 手动操作的才能 更新 RunParams， RunParams 提供前端查询实时进度
	if c.TaskFrom == "manual" && taskType != types.SpanCalibrate && taskType != types.ZeroCalibrate {
		hub.TaskManager.SetTaskParams(c.Params)
	}
}

//code 0 初始状态, 1 成功, 2 失败
//msg 错误消息
//taskName 任务昵称
//taskType 任务类型
//event 事件
//currNum 进度
//total 总任务
//addLog 添加日志。
func (c *CaliTasks) addLog(code int, msg string, taskName string, taskType string, painType string, event string, currNum int, total int, pid string) (string, error) {
	c.Params["request_id"] = c.requestId
	c.Params["model_id"] = c.modelId
	c.Params["code"] = code
	c.Params["msg"] = msg
	id, _ := common.GetSnowflakeId()
	t := time.Now()
	data := model.TaskLog{
		ID:         strconv.FormatInt(id, 10),
		ThingId:    c.thingId,
		StationId:  c.stationId,
		TaskType:   taskType,
		Status:     code,
		ProcessNum: total,
		CurrNum:    currNum,
		Msg:        msg,
		TaskName:   taskName,
		PlanType:   painType,
		TaskStatus: "doing",
		Pid:        pid,
		CreatedAt:  model.Datetime(t),
		UpdatedAt:  model.Datetime(t),
	}
	hub.TaskManager.SetTaskCurr(c.Curr)
	hub.TaskManager.SetTaskTotal(total)
	// 手动操作的才能 更新 RunParams， RunParams 提供前端查询实时进度
	c.SetManualRunParams(taskType)
	b, _ := json.Marshal(c.Params)
	_ = data.Content.Scan(b)
	taskLogId, err := model.Dao.TTask.Add(data)
	if code == InitialStatus && pid != "" {
		// 更新父任务的状态
		c.modifyLog(code, msg, pid, taskName, taskType, event, nil)
	}
	//同步记录到云端
	sendCloudAddTaskLog(data)
	return taskLogId, err
}

//runSubTask 运行子任务
//taskId 任务ID
//taskType 任务类型
//params 校准参数
//index 任务组具体的下标.
func (c *CaliTasks) runSubTask(taskId, groupId string, taskType string, params types.CaliParams, index int, isConfirm bool) error {
	// 开始执行子任务 start
	//var caliFlow = types.CaliFlowDefault[c.modelId]
	var caliFlow = getCaliFlow(c.modelId, params)
	taskLogId, err := addSubTaskLog(taskId, groupId, index, c.modelId, params.GasType, params.Ext, getGasNum(c.modelId, params), caliFlow, types.GetUnit(c.modelId), InitialStatus, nil)
	if err != nil {
		logger.SLog.Errorf("[%s]  addSubTaskLog err:=%v", c.requestId, err)
		return err
	}

	// 开始下发命令
	errCode, err := c.startTask(params, taskType, caliFlow)
	if errCode != types.Success {
		logger.SLog.Errorf("[%s]  c.startTask errCode= %v, msg=%v, err:=%v", c.requestId, errCode, types.Msg[errCode], err)
		//修改子任务状态
		modifySubTaskLog(taskLogId, FailureStatus, nil, types.Status, []float64{})
		return errors.New("网络异常,启动任务失败")
	}
	// 记录正在通气的状态
	hub.TaskManager.SetGasType(params.GasType)

	paramValue := make([]float64, 0, 3)
	////todo test
	//timer := time.NewTicker(time.Second)
	timer := time.NewTicker(time.Minute)
	defer timer.Stop()
	hourMeter := 0
	for {
		select {
		case <-timer.C:
			hourMeter += 1
			//指定具体的时间采集数据
			if len(params.ValueTime) > 0 {
				if isExist(hourMeter, params.ValueTime) {
					value, err := c.dataCollection(taskType, taskId, params)
					if err != nil {
						logger.SLog.Errorf("[%s]  c.dataCollection msg=%v", c.requestId, "数据采集失败.")
						//这里需要改成修改子任务状态
						modifySubTaskLog(taskLogId, FailureStatus, err, types.Status, []float64{})
						c.modifyLog(FailureStatus, "数据采集失败.", taskId, "", taskType, types.End, err)
						return errors.New("数据采集失败")
					}
					paramValue = append(paramValue, value)
					break
				}
			}
			// 5分钟之后开始采集   每分钟采集一次
			if params.EveryTime > 0 && hourMeter > 30 && hourMeter%params.EveryTime == 0 {
				value, err := c.dataCollection(taskType, taskId, params)
				if err != nil {
					logger.SLog.Errorf("[%s]  c.dataCollection msg=%v", c.requestId, "数据采集失败.")
					//这里需要改成修改子任务状态
					modifySubTaskLog(taskLogId, FailureStatus, err, types.Status, []float64{})
					c.modifyLog(FailureStatus, "数据采集失败.", taskId, "", taskType, types.End, err)
					return errors.New("数据采集失败")
				}
				paramValue = append(paramValue, value)
				break
			}
			if hourMeter < params.Ext {
				break
			}

			// 子任务执行结束 end
			var resp int
			var event string
			if isConfirm {
				// 发送确认校准
				event = types.Confirm
				resp, err = c.confirmCali(params, taskType)
			} else {
				// 发送取消校准
				event = types.End
				resp, err = c.endTask(params, taskType)
			}
			if resp != types.Success {
				//这里需要改成修改子任务状态 (失败)
				modifySubTaskLog(taskLogId, FailureStatus, err, event, paramValue)
				c.modifyLog(FailureStatus, types.Msg[resp], taskId, "", taskType, types.End, err)
				return errors.New(types.Msg[resp])
			} else {
				//这里需要改成修改子任务状态 (成功)
				modifySubTaskLog(taskLogId, SuccessStatus, err, event, paramValue)
				c.modifyLog(InitialStatus, "", taskId, "", taskType, types.End, err)
				return nil
			}
		case <-c.ctx.Done():
			//手动取消任务
			c.endTask(params, taskType)
			modifySubTaskLog(taskLogId, FailureStatus, errors.New("手动取消任务"), types.End, paramValue)
			// 执行 7 天 的任务，只取消当天的
			c.modifyLog(CancelStatus, "取消任务", taskId, "", taskType, types.End, errors.New("手动取消任务"))
			return StopTaskErr
		}
	}
	return nil
}
